package cn.macswelle.campusecurity.nvrlistener.service;

import cn.macswelle.campusecurity.common.dto.requestDto.FaceDto;
import cn.macswelle.campusecurity.sdk.DeviceInfo;
import cn.macswelle.campusecurity.sdk.service.AmqpService;
import cn.macswelle.campusecurity.sdk.service.DeviceThread;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacv.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * 视频的处理与推流服务，作为生命周期独立的线程。
 * 交给spring管理，但启动这个线程之前一定要先init()或者执行其有参构造器
 * 使用sdk提供的线程管理方法
 * 注意是多例模式，即任何一个实例都是新的实例，不能直接注入，要用beanFactory
 *
 * @author Ge Hanchen
 * @version 2.0
 */
@Service
@Scope("prototype")
public class VirtualCameraService2 extends DeviceThread {
  private OpenCVFrameGrabber grabber = null;
  private FrameRecorder recorder;
  private boolean init = false;
  private CanvasFrame canvasFrame;
  private Frame frame;
  private opencv_core.IplImage image;
  private OpenCVFrameConverter.ToIplImage converter = null;
  private int a = 0;

  @Autowired
  private AmqpService amqpService;

  @Autowired
  private DeviceInfo deviceInfo;

  public void init(int num, String url, String rate) throws FrameGrabber.Exception, FrameRecorder.Exception {
    info = "cam" + num + " " + url;
    logger.info(info + " 正在初始化");
    canvasFrame = new CanvasFrame(info);
    canvasFrame.setVisible(true);
    grabber = new OpenCVFrameGrabber(num);
    grabber.start();
    frame = grabber.grab();
    converter = new OpenCVFrameConverter.ToIplImage();
    image = converter.convert(frame);
    recorder = FrameRecorder.createDefault(url, image.width(), image.height());
    recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
    recorder.setFormat("flv");
    recorder.setFrameRate(Double.parseDouble(rate));
    recorder.start();
    init = true;
  }

  @Override
  public void execute() {
    try {
      if ((frame = grabber.grab()) == null) {
        running = false;
        return;
      }
      canvasFrame.showImage(frame);
      if (a % 5000 == 0) {
        a = 0;//每隔200帧进行一次识别，此处要加入消息队列
        logger.info(info + " 执行识别");
        Java2DFrameConverter java2dFrameConverter = new Java2DFrameConverter();
        searchFace(java2dFrameConverter.getBufferedImage(frame));
      }
      image = converter.convert(frame);
      Frame rotatedFrame = converter.convert(image);
      if (running && rotatedFrame != null) recorder.record(rotatedFrame);
      a++;
    } catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
      logger.info(info + " 视频处理中断，视频流提前关闭");
    }
  }

  @Override
  public boolean preExecute() {
    return init;
  }

  @Override
  public void postExecute() {
    logger.info(info + " 视频处理线程关闭");
  }

  private void searchFace(BufferedImage image) {
    FaceDto faceDto = new FaceDto();
    faceDto.setImage(bufferedImageToBase64(image));
    faceDto.setTime(System.currentTimeMillis());
    faceDto.setLocation(deviceInfo.getLocation());
    amqpService.sendFace(false, faceDto);
  }

  public static String bufferedImageToBase64(BufferedImage image) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try {
      ImageIO.write(image, "jpg", outputStream);
    } catch (IOException e) {
      e.printStackTrace();
    }
    BASE64Encoder encoder = new BASE64Encoder();
    return encoder.encode(outputStream.toByteArray());
  }

  public void stop() throws FrameGrabber.Exception, FrameRecorder.Exception {
    logger.info(info + " 试图停止");
    this.running = false;
    this.canvasFrame.setVisible(false);
//    Thread.sleep(500);//run是volatile后，立刻修改主存，就不需要等待线程停止了
    this.grabber.stop();
    this.recorder.close();
  }

  public void restart(String url, String rate) throws FrameGrabber.Exception, FrameRecorder.Exception {
    logger.info(info + " 试图重新启动");
    if (!running) {
      this.running = true;
      this.grabber.start();
      this.frame = grabber.grab();
      this.image = converter.convert(frame);
      this.canvasFrame.setVisible(true);
      recorder = FrameRecorder.createDefault(url, image.width(), image.height());
      recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
      recorder.setFormat("flv");
      recorder.setFrameRate(Double.parseDouble(rate));
      this.recorder.start();
      new Thread(this).start();
    }
  }

  @Override
  public String toString() {
    return info + " running:" + running;
  }
}
